import os

import joblib
import pandas as pd
import numpy as np
import lightgbm as lgb
import sklearn.metrics
import xgboost as xgb
import catboost as cbt
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.views import View
from django.conf import settings
from numpy import matrix
from cvxopt import matrix, solvers

from sklearn.metrics import f1_score

# Create train views here.
# 将上传的文件名作为全局变量保存
file_name = ''


# 上传训练集，接收训练集
class Upload(View):
    """训练功能：上传训练集"""

    def get(self, request):
        context = {
            'describe': '训练集',
        }
        return render(request, 'train/train-upload.html', context=context)

    def post(self, request):
        global file_name

        csv_file = request.FILES.get('csv_file')
        file_name = csv_file.name
        if file_name.split('.')[1] != 'csv':
            return JsonResponse({'code': 0})

        file_path = os.path.join(settings.MEDIA_ROOT, 'csv_dir\\' + file_name)

        # 学到
        with open(file_path, 'wb+') as f:
            for chunk in csv_file.chunks():
                f.write(chunk)

        return JsonResponse({'code': 1})


# 执行训练
def execute_train(request):

    # 读入 CSV 文件
    df = pd.read_csv(os.path.join(settings.MEDIA_ROOT, 'csv_dir\\' + file_name))  # 想训练的csv文件的文件名

    # 获取每列的平均值，并用该列的平均值填充空值和 NaN 值
    df = df.apply(lambda x: x.fillna(x.mean()), axis=0)
    Xtrain = df.iloc[::, 1:108:]
    Ytrain = df.iloc[:, -1]
    sample_size = len(Xtrain)
    feature_quantity = len(Xtrain.columns)

    # 开始训练
    # 1、LightGBM
    lg = lgb.LGBMClassifier()
    lg.fit(Xtrain, Ytrain)
    # 2、xgboost
    xg = xgb.XGBClassifier()
    xg.fit(Xtrain, Ytrain)
    # 3、catboost
    cb = cbt.CatBoostClassifier()
    cb.fit(Xtrain, Ytrain)

    # 生成模型
    joblib.dump(lg, os.path.join(settings.MEDIA_ROOT, 'model\\' + 'LightGBM'))
    joblib.dump(xg, os.path.join(settings.MEDIA_ROOT, 'model\\' + 'XgBoost'))
    joblib.dump(cb, os.path.join(settings.MEDIA_ROOT, 'model\\' + 'CatBoost'))

    context = {
        'sample_size': sample_size,
        'feature_quantity': feature_quantity,
    }

    # 返回结果给可视化模版
    response = render(request, 'train/train-visualization.html', context=context)
    train_count = int(request.COOKIES.get('train'))
    train_count += 1
    response.set_cookie('train', train_count)
    return response


# 开放下载模型
def download(request):
    # 读取要下载的文件
    filepath = ''

    choose = request.GET.get('choose')
    if choose == 'LightGBM':
        filepath = os.path.join(settings.MEDIA_ROOT, 'model\\' + 'LightGBM')
    elif choose == 'XgBoost':
        filepath = os.path.join(settings.MEDIA_ROOT, 'model\\' + 'XgBoost')
    elif choose == 'CatBoost':
        filepath = os.path.join(settings.MEDIA_ROOT, 'model\\' + 'CatBoost')

    with open(filepath, 'rb') as f:
        filedata = f.read()

    # 返回响应，弹出文件下载对话框
    response = HttpResponse(filedata, content_type='application/octet-stream')
    response['Content-Disposition'] = f'attachment; filename="{choose}"'

    return response


def kernel(ker, X1, X2, gamma):
    K = None
    if ker == 'linear':
        if X2 is not None:
            K = sklearn.metrics.pairwise.linear_kernel(
                np.asarray(X1), np.asarray(X2))
        else:
            K = sklearn.metrics.pairwise.linear_kernel(np.asarray(X1))
    elif ker == 'rbf':
        if X2 is not None:
            K = sklearn.metrics.pairwise.rbf_kernel(
                np.asarray(X1), np.asarray(X2), gamma)
        else:
            K = sklearn.metrics.pairwise.rbf_kernel(
                np.asarray(X1), None, gamma)
    return K


class KMM:
    def __init__(self, kernel_type='linear', gamma=1.0, B=1.0, eps=None):
        '''
        Initialization function
        :param kernel_type: 'linear' | 'rbf'
        :param gamma: kernel bandwidth for rbf kernel
        :param B: bound for beta
        :param eps: bound for sigma_beta
        '''
        self.kernel_type = kernel_type
        self.gamma = gamma
        self.B = B
        self.eps = eps

    def fit(self, Xs, Xt):
        '''
        Fit source and target using KMM (compute the coefficients)
        :param Xs: ns * dim
        :param Xt: nt * dim
        :return: Coefficients (Pt / Ps) value vector (Beta in the paper)
        '''
        ns = Xs.shape[0]
        nt = Xt.shape[0]
        if self.eps == None:
            self.eps = self.B / np.sqrt(ns)
        K = kernel(self.kernel_type, Xs, None, self.gamma)
        kappa = np.sum(kernel(self.kernel_type, Xs, Xt, self.gamma)
                       * float(ns) / float(nt), axis=1)

        K = matrix(K.astype(np.double))
        kappa = matrix(kappa.astype(np.double))
        G = matrix(np.r_[np.ones((1, ns)), -
                   np.ones((1, ns)), np.eye(ns), -np.eye(ns)])
        h = matrix(np.r_[ns * (1 + self.eps), ns * (self.eps - 1),
                   self.B * np.ones((ns,)), np.zeros((ns,))])

        sol = solvers.qp(K, -kappa, G, h)
        beta = np.array(sol['x'])
        return beta
